/*
 * Copyright (c) Jiandong.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { YogaConstants } from '../../yoga/YogaConstants';
import { YogaNode } from '../../yoga/YogaNode';

export class Spacing {

  /** Spacing type that represents the left direction. E.g. {@code marginLeft}. */
  public static readonly LEFT = 0;
  /** Spacing type that represents the top direction. E.g. {@code marginTop}. */
  public static readonly TOP = 1;
  /** Spacing type that represents the right direction. E.g. {@code marginRight}. */
  public static readonly RIGHT = 2;
  /** Spacing type that represents the bottom direction. E.g. {@code marginBottom}. */
  public static readonly BOTTOM = 3;
  /**
   * Spacing type that represents start direction e.g. left in left-to-right, right in
   * right-to-left.
   */
  public static readonly START = 4;
  /**
   * Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left.
   */
  public static readonly END = 5;
  /**
   * Spacing type that represents horizontal direction (left and right). E.g. {@code
   * marginHorizontal}.
   */
  public static readonly HORIZONTAL = 6;
  /**
   * Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}.
   */
  public static readonly VERTICAL = 7;
  /**
   * Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}.
   */
  public static readonly ALL = 8;

  private static readonly FlagsMap = [
    1, /*LEFT*/ 2, /*TOP*/ 4, /*RIGHT*/ 8, /*BOTTOM*/ 16, /*START*/ 32, /*END*/ 64, /*HORIZONTAL*/128, /*VERTICAL*/ 256, /*ALL*/
  ];

  private readonly mSpacing: number[];
  private mValueFlags = 0;
  private readonly mDefaultValue: number;
  private mHasAliasesSet: boolean;

  public constructor(defaultValue: number) {
    this.mDefaultValue = defaultValue;
    this.mSpacing = Spacing.newFullSpacingArray();
  }

//  public Spacing(original: Spacing) {
//    mDefaultValue = original.mDefaultValue;
//    mSpacing = Arrays.copyOf(original.mSpacing, original.mSpacing.length);
//    mValueFlags = original.mValueFlags;
//    mHasAliasesSet = original.mHasAliasesSet;
//  }

/**
 * Set a spacing value.
 *
 * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link
 *     #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
 * @param value the value for this direction
 * @return {@code true} if the spacing has changed, or {@code false} if the same value was already
 *     set
 */
  public set(spacingType: number, value: number) {
    if (this.mSpacing[spacingType] != value) {
      this.mSpacing[spacingType] = value;

    if (YogaConstants.isUndefined(value)) {
      this.mValueFlags &= ~Spacing.FlagsMap[spacingType];
    } else {
      this.mValueFlags |= Spacing.FlagsMap[spacingType];
    }

    this.mHasAliasesSet =
    (this. mValueFlags& Spacing.FlagsMap[Spacing.ALL]) != 0
    || (this. mValueFlags& Spacing.FlagsMap[Spacing.VERTICAL]) != 0
    || (this. mValueFlags& Spacing.FlagsMap[Spacing.HORIZONTAL]) != 0;

      return true;
    }

    return false;
  }

  /**
   * Get the spacing for a direction. This takes into account any default values that have been set.
   *
   * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
   */
  public get(spacingType: number): number {
      let defaultValue =
          (spacingType == Spacing.START || spacingType == Spacing.END ? YogaConstants.UNDEFINED : this.mDefaultValue);

      if (this.mValueFlags == 0) {
        return defaultValue;
      }

      if ((this.mValueFlags & Spacing.FlagsMap[spacingType]) != 0) {
        return this.mSpacing[spacingType];
      }

      if (this.mHasAliasesSet) {
          let secondType = spacingType == Spacing.TOP || spacingType == Spacing.BOTTOM ? Spacing.VERTICAL : Spacing.HORIZONTAL;
          if ((this.mValueFlags & Spacing.FlagsMap[secondType]) != 0) {
          return this.mSpacing[secondType];
          } else if ((this.mValueFlags & Spacing.FlagsMap[Spacing.ALL]) != 0) {
          return this.mSpacing[Spacing.ALL];
      }
    }

    return defaultValue;
  }

  /**
   * Get the raw value (that was set using {@link #set(int, float)}), without taking into account
   * any default values.
   *
   * @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link
   *     #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
   */
  public getRaw(spacingType: number): number {
    return this.mSpacing[spacingType];
  }

  /**
   * Resets the spacing instance to its default state. This method is meant to be used when
   * recycling {@link Spacing} instances.
   */
  public reset() {
  //  Arrays.fill(mSpacing, YogaConstants.UNDEFINED);
  //  this.mSpacing.map((value)=> YogaConstants.UNDEFINED )
    this.mSpacing.map((value) => 0 )
    this.mHasAliasesSet = false;
    this.mValueFlags = 0;
  }

  /**
   * Try to get start value and fallback to given type if not defined. This is used privately by the
   * layout engine as a more efficient way to fetch direction-aware values by avoid extra method
   * invocations.
   */
  getWithFallback(spacingType: number, fallbackType: number): number {
    return (this.mValueFlags & Spacing.FlagsMap[spacingType]) != 0 ? this.mSpacing[spacingType] : this.get(fallbackType);
  }

  private static newFullSpacingArray(): number[] {
    return [
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
      YogaConstants.UNDEFINED,
    ];
  }
}
